package com.rankeiot.core.anno.aspect;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.rankeiot.core.anno.RLock;
import com.rankeiot.core.exception.BusinessException;
import com.rankeiot.core.util.StringUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.CodeSignature;
import org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 重入锁,同名锁使用10分钟后可能会失效
 */
@Aspect
public class RLockAspect extends AbstractExpressionAspect {
    public interface LockManager {
        Lock getLock(String key);
    }

    public static class LocalLockManager implements LockManager {
        private Cache<String, ReentrantLock> locks = Caffeine.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES).build();//new ConcurrentHashMap<>();

        public synchronized Lock getLock(String key) {
            ReentrantLock lock = locks.getIfPresent(key);
            if (lock != null) {
                return lock;
            }
            lock = new ReentrantLock();
            locks.put(key, lock);
            return lock;
        }
    }

    @Autowired
    LockManager lockManager;
    ExpressionParser parser = new SpelExpressionParser();

    @Around("@annotation(rLock)")
    public Object aroundRLock(ProceedingJoinPoint point, RLock rLock) throws Throwable {
        StandardEvaluationContext ctx = new StandardEvaluationContext();
        String lockV = executeExpression(point,ctx,rLock.value());
        Lock lock = lockManager.getLock(lockV);
        String message= rLock.message();
        if (!lock.tryLock()) {
            throwBusinessException(point,message);
        }
        try {
            return point.proceed();
        } finally {
            lock.unlock();
        }
    }
}
